Skip to content

fix(sync): resume ended episodes from synced progress - #464

Merged
chhoumann merged 3 commits into
masterfrom
fix/ended-relisten-handoff
Oct 13, 2026
Merged

chhoumann merged 3 commits into
masterfrom
fix/ended-relisten-handoff

Conversation

@chhoumann

@chhoumann chhoumann commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

Summary

Fixes #463.

After an episode ends on this device, Play now resumes unfinished progress synced from another device. A saved finished episode still restarts at zero. Both Play entry points use the existing saved-progress policy and respect a rejected seek past the local media duration.

The playing-sync journey flushes pending progress before sync and checks both time bounds. It catches the V-462 mutation that previously survived.

Verification

  • All passed under Node 22.23.2: npm run lint, npm run format:check, npm run typecheck, npm run build, npm run test with 2,432 tests, npm run test:tooling with 13 tests, and npm run docs:build.
  • npm run test:e2e passed all 141 tests on Linux Obsidian 1.14.4. PODNOTES_OBSIDIAN_APP=/opt/Obsidian/obsidian npm run test:e2e passed all 141 on 1.13.7. Both full runs used fresh vaults at the final commit.
  • Live verification used the real 60-second Fixtures/Local Fixture.wav. After natural end, sync to 30 seconds and flush, visible Play resumed at 30. Master restarted near zero. Unit and E2E regressions failed on master. The strengthened journey failed with the exact V-462 mutation and passed without it.
  • npm run count:writes -- start, report, and stop matched master at every checkpoint. npm run size passed at 311,774 raw and 98,336 gzip bytes, increases of 30 and 12 bytes.

Full verification report and evidence archive contain commands, persisted values, review findings, and the verification deviation.

Master after Play Fixed after Play
Master restarts near zero Fixed playback resumes at 30 seconds

Release impact

No schema, settings, API or URI change. No migration.

Docs at release

In #458's sync handoff and mobile guidance, add that an episode ended here resumes unfinished synced progress when the user presses Play. A saved finished episode still restarts at zero. The ended player may show its old end position until Play.

Retain #462's immediate adoption for a clean paused player, the segment-preview exception, waiting for sync, and selecting the desired episode when a different episode is loaded.

Note

Fix ended-episode replay to resume from synced progress

  • PlayerController.play() now resumes a naturally ended episode from resumeTime(episode) when no explicit time is given; an explicit time still takes precedence and marks the position as user-moved
  • PlayerController.resume() on an ended loaded session moves to resumeTime(episode) instead of unconditionally restarting at zero
  • Adds unit, controller, and end-to-end coverage in PlayerController.test.ts and synced-progress-handoff.test.ts, including shorter-copy clamping and explicit-time precedence cases
  • Behavioral Change: replaying an ended episode with a finished record still restarts at zero; an unfinished synced position now wins over the ended state

Macroscope summarized c52b569.

…tural end

Play and resume() on a naturally ended episode restart at 0 even when
another device has since saved unfinished progress for it, and the
first pause then saves over that progress (#463).

The unit tests cover both entry points, a finished episode that starts
over, a synced position past the end of the local copy, which must
pause short of the end without finishing, and an explicit time, which
wins over synced progress. The ended journey pauses with the visible
toggle and checks the saved position. The playing-sync journey checks
that the remote position merges and playback keeps advancing.
Play and resume() on a naturally ended episode now start at its saved
progress, the same position a new load of it starts at, instead of 0.
A finished, past-duration or missing record still starts over, and an
explicit time still wins. A synced position at or past the end of the
local copy pauses short of the end, as a seek does, instead of playing
the last moment and finishing the episode (#463).
The journey now flushes local saves, rewrites data.json through the
vault adapter, awaits onExternalSettingsChange() and returns the merged
progress from the same renderer eval. The separate read after the merge
poll could run after the player's next position save had replaced the
synced time.
@chhoumann
chhoumann merged commit b14edf1 into master Oct 13, 2026
18 checks passed
@chhoumann
chhoumann deleted the fix/ended-relisten-handoff branch October 13, 2026 23:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

An ended episode restarts from zero after a synced re-listen

1 participant